;;; -*- Mode: Lisp; Package: CCL; Coding: utf-8; -*-

(chapter "Platform-specific notes"
  (defsection "Overview"
    (para " The documentation and whatever experience you may have in
      using {CCL} under Linux should also apply to using it under
      Darwin/MacOS X and FreeBSD. There are some differences between
      the platforms, and these differences are sometimes exposed in
      the implementation.")
    
    (defsection "File-system case"
      #:|Darwin and MacOS X use HFS+ file systems by default;
	    HFS+ file systems are usually case-insensitive. Most of
	    {CCL}'s filesystem and pathname code assumes that the
	    underlying filesystem is case-sensitive; this assumption
	    extends to functions like EQUAL, which assumes that #p"FOO"
	    and #p"foo" denote different, un-EQUAL filenames. Since
	    Darwin/MacOS X can also use UFS and NFS filesystems, the
	    opposite assumption would be no more correct than the one
	    that's currently made.

        Whatever the best solution to this problem turns out to
        be, there are some practical considerations. Doing:|
      (code-block #:|
? (save-application "DPPCCL")
	  |)
      #:|on 32-bit DarwinPPC has the unfortunate side-effect of
        trying to overwrite the Darwin {CCL} kernel, "dppccl", on a
        case-insensitive filesystem.

        To work around this, the Darwin {CCL} kernel expects
        the default heap image file name to be the kernel's own
        filename with the string ".image" appended, so the idiom would
        be:|
      (code-block #:|
? (save-application "dppccl.image")
	  |))
    (defsection "Line Termination Characters"
      #:|MacOSX effectively supports two distinct line-termination
	    conventions. Programs in its Darwin substrate follow the Unix
	    convention of recognizing #\\LineFeed as a line terminator; traditional
	    MacOS programs use #\\Return for this purpose.  Many modern
	    GUI programs try to support several different line-termination
	    conventions (on the theory that the user shouldn't be too concerned
	    about what conventions are used an that it probably doesn't matter.
	    Sometimes this is true, other times ... not so much.
	  

        {CCL} follows the Unix convention on both Darwin and
        LinuxPPC, but offers some support for reading and writing
        files that use other conventions (including traditional MacOS
        conventions) as well.

	    This support (and anything like it) is by nature
	    heuristic: it can successfully hide the distinction between
	    newline conventions much of the time, but could mistakenly
	    change the meaning of otherwise correct programs (typically
	    when files contain both #\\Return and #\\Linefeed characters or
	    when files contain mixtures of text and binary data.) Because
	    of this concern, the default settings of some of the variables
	    that control newline translation and interpretation are
	    somewhat conservative.

	    Although the issue of multiple newline conventions
	    primarily affects MacOSX users, the functionality described
	    here is available under LinuxPPC as well (and may occasionally
	    be useful there.)

	    None of this addresses issues
	    related to the third newline convention ("CRLF") in widespread
	    use (since that convention isn't native to any platform on
	    which {CCL} currently runs). If {CCL} is ever ported to
	    such a platform, that issue might be revisited.

	    Note that some MacOS programs (including some versions
	    of commercial MCL) may use HFS file type information to
	    recognize TEXT and other file types and so may fail to
	    recognize files created with {CCL} or other Darwin
	    applications (regardless of line termination issues.)

	    Unless otherwise noted, the symbols mentioned in this
	    documentation are exported from the CCL package.|)
    (defsection "Single-precision trig & transcendental functions"
      (para "
	    Despite what Darwin's man pages say, early versions of its math library
	    (up to and including at least OSX 10.2 (Jaguar) don't implement
	    single-precision variants of the transcendental and trig functions
	    (#_sinf, #_atanf, etc.) {CCL} worked around this by coercing
	    single-precision args to double-precision, calling the
	    double-precision version of the math library function, and coercing
	    the result back to a SINGLE-FLOAT. These steps can introduce rounding
	    errors (and potentially overflow conditions) that might not be present
	    or as severe if true 32-bit variants were available."))
    (defsection "Shared libraries"
      (para #:|Darwin/MacOS X distinguishes between "shared libraries"
        and "bundles" or "extensions"; Linux and FreeBSD don't. In
        Darwin, "shared libraries" have the file type "dylib" : the
        expectation is that this class of file is linked against when
        executable files are created and loaded by the OS when the
        executable is launched. The latter class -
        "bundles/extensions" - are expected to be loaded into and
        unloaded from a running application, via a mechanism like the
        one used by {CCL}'s OPEN-SHARED-LIBRARY function.|)))

  (defsection "Cocoa Programming in {CCL}"
    "Cocoa is one of Apple's APIs for GUI programming; for most
      purposes, development is considerably faster with Cocoa than
      with the alternatives.  You should have a little familiarity
      with it, to better understand this section.

      A small sample Cocoa program can be invoked by evaluating
      (REQUIRE 'TINY) and then (CCL::TINY-SETUP). This program
      provides a simple example of using several of the bridge's
      capabilities.

      The Tiny demo creates Cocoa objects dynamically, at
      runtime, which is always an option.  However, for large
      applications, it is usually more convenient to create your
      objects with Apple Interface Builder, and store them in .nib
      files to be loaded when needed.  Both approaches can be freely
      mixed in a single program."
    (defsection "The Command Line and the Window System"
      "{CCL} is ordinarily a command-line application (it
        doesn't have a connection to the OSX Window server, doesn't
        have its own menubar or dock icon, etc.) By opening some
        libraries and jumping through some hoops, it's able to sort of
        transform itself into a full-fledged GUI application (while
        retaining its original TTY-based listener.) The general idea
        is that this hybrid environment can be used to test and
        prototype UI ideas and the resulting application can eventually
        be fully transformed into a bundled, double-clickable
        application. This is to some degree possible, but there needs
        to be a bit more infrastructure in place before many people
        would find it easy.

        Cocoa applications use the NSLog function to write
        informational/warning/error messages to the application's
        standard output stream. When launched by the Finder, a GUI
        application's standard output is diverted to a logging
        facility that can be monitored with the Console application
        (found in /Applications/Utilities/Console.app).  In the hybrid
        environment, the application's standard output stream is
        usually the initial listener's standard output stream. With
        two different buffered stream mechanisms trying to write to
        the same underlying Unix file descriptor, it's not uncommon to
        see NSLog output mixed with lisp output on the initial
        listener.")
    (defsection "Writing (and reading) Cocoa code"
      #:|The
	    syntax of the constructs used to define Cocoa classes and
	    methods has changed a bit (it was never documented outside of
	    the source code and never too well documented at all), largely
	    as the result of functionality offered by Randall Beer's
	    bridge; the "standard name-mapping conventions"
	    referenced below are described in his CocoaBridgeDoc.txt file,
	    as are the constructs used to invoke ("send messages
	    to") Cocoa methods.

        All of the symbols described below are currently internal to
        the CCL package.|
      (listing :column
	(item (ref (definition :macro objc:@class)))
	(item (ref (definition :macro objc:@selector)))
	(item (ref (definition :macro objc:define-objc-method)))
	(item (ref (definition :macro objc:define-objc-class-method)))))
    (defsection "The Application Kit and Multiple Threads"
      #:|The Cocoa API is broken into several pieces.  The
        Application Kit, affectionately called AppKit, is the one
        which deals with window management, drawing, and handling
        events.  AppKit really wants all these things to be done by a
        "distinguished thread".  creation, and drawing to take place
        on a distinguished thread.

        Apple has published some guidelines which discuss these
        issues in some detail; see the Apple Multithreading
        Documentation, and in particular the guidelines on Using the
        Application Kit from Multiple Threads.  The upshot is that
        there can sometimes be unexpected behavior when objects are
        created in threads other than the distinguished event thread;
        eg, the event thread sometimes starts performing operations on
        objects that haven't been fully initialized.

        It's
        certainly more convenient to do certain types of exploratory
        programming by typing things into a listener or evaluating a
        "defun" in an Emacs buffer; it may sometimes be
        necessary to be aware of this issue while doing so.

        Each thread in the Cocoa runtime system is expected to
        maintain a current "autorelease pool" (an instance
        of the NSAutoreleasePool class); newly created objects are
        often added to the current autorelease pool (via the
        -autorelease method), and periodically the current autorelease
        pool is sent a "-release" message, which causes it
        to send "-release" messages to all of the objects
        that have been added to it.

        If the current thread doesn't have a current autorelease
        pool, the attempt to autorelease any object will result in a
        severe-looking warning being written via NSLog. The event
        thread maintains an autorelease pool (it releases the current
        pool after each event is processed and creates a new one for
        the next event), so code that only runs in that thread should
        never provoke any of these severe-looking NSLog
        messages.

        To try to suppress these messages (and
        still participate in the Cocoa memory management scheme), each
        listener thread (the initial listener and any created via the
        "New Listener" command in the IDE) is given a
        default autorelease pool; there are REPL colon-commands for
        manipulating the current listener's "toplevel
        autorelease pool".

        In the current scheme, every time that Cocoa calls lisp
        code, a lisp error handler is established which maps any lisp
        conditions to ObjC exceptions and arranges that this exception
        is raised when the callback to lisp returns. Whenever lisp
        code invokes a Cocoa method, it does so with an ObjC exception
        handler in place; this handler maps ObjC exceptions to lisp
        conditions and signals those conditions.

        Any
        unhandled lisp error or ObjC exception that occurs during the
        execution of the distinguished event thread's event loop
        causes a message to be NSLog'ed and the event loop to (try to)
        continue execution. Any error that occurs in other threads is
        handled at the point of the outermost Cocoa method
        invocation. (Note that the error is not necessarily
        "handled" in the dynamic context in which it
        occurs.)

        Both of these behaviors could possibly be improved; both of them
        seem to be substantial improvements over previous behaviors (where,
        for instance, a misspelled message name typically terminated the
        application.)|)
    (defsection "Acknowledgement"
      (para "The Cocoa bridge was originally developed, and
        generously contributed by, Randall Beer.")))
  (defsection "Building an Application Bundle"
    (para #:|You may have noticed that (require "COCOA") takes a long
      time to load.  It is possible to avoid this by saving a Lisp
      heap image which has everything already loaded.  There is an
      example file which allows you to do this,
      "ccl/examples/cocoa-application.lisp", by producing a
      double-clickable application which runs your program.  First,
      load your own program.  Then, do:|)
    (code-block #:|
? (require "COCOA-APPLICATION")
    |)
    "When it finishes, you should be able to double-click the {CCL} icon
      in the ccl directory, to quickly start your program.

      The OS may have already decided that {CCL}.app isn't a valid
      executable bundle, and therefore won't let you double-click it.
      If this happens to you, to force it to reconsider, just update the
      last-modified time of the bundle.  In Terminal:"
    (code-block "> touch {CCL}.app
    ")
    (para #:|When an image which had contained ObjC classes (which are also
      CLOS classes) is re-launched, those classes are "revived": all
      preexisting classes have their addresses updated destructively, so that
      existing subclass/superclass/metaclass relationships are maintained.
      It's not possible (and may never be) to preserve foreign
      instances across SAVE-APPLICATION. (It may be the case that NSArchiver
      and NSCoder and related classes offer some approximation of that.)|))
  (defsection "Recommended Reading"
    (listing :definition
      (item
	(clause
	  "{link http://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/Cocoa.html
                Cocoa Core Competencies
              }, {link http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/WhatIsCocoa/WhatIsCocoa.html
                Cocoa Fundamentals Guide
              }")
	ccldoc::=> "
	        These are top-level pages pertaining to Cocoa in Apple's Mac OS X Developer Library.
	        If you are unfamiliar with Cocoa, these links are good places to start.
	      ")
      (item
	(clause
	  "{link http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Introduction/introObjectiveC.html
                The Objective-C Programming Language
              }, {link http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html
                Objective-C Runtime Programming Guide
              }")
	ccldoc::=>
	"These provide a conceptual overview and programming guide to Objective-C the language and runtime, respectively.")
      (item
	"{link https://developer.apple.com/documentation/objectivec/objective-c_runtime
                Objective-C Runtime Reference
              }"
	ccldoc::=> "
	        This is one of the two most important Cocoa references; it
	        covers all of the basics, except for GUI programming.  This is
	        a reference, not a tutorial.
	      ")
      (item
	"{link https://developer.apple.com/documentation/appkit Application Kit Framework Reference}"
	ccldoc::=> "
	        This is the other very important Cocoa reference; it covers GUI programming with Cocoa / Application Kit Framework
	        in considerable depth.  This is a reference, not a tutorial.
	      ")
      (item "{link http://developer.apple.com/library/mac/navigation/ Mac OS X Developer Library}" ccldoc::=> "
	        This is the top page for Mac OS X developer documentation.
	        Go here to find the documentation on any other Mac OS X API.
	        Also go here if you need general guidance about OS X, Carbon,
	        Cocoa, Core Foundation, or Objective-C.
	      ")
      (item "{link http://developer.apple.com/ Resources for Apple Developers}" ccldoc::=> "
                This is the top page for all Apple developer documentation.
              ")))
  (defsection "Operating-System Dictionary"

    (definition (:macro objc:@class) "@class class-name" nil
      (defsection "Arguments and Values"
	(listing :definition
	  (item "{param class-name}" ccldoc::=> "a string which denotes an existing class name, or a
		        symbol which can be mapped to such a string via the standard
		        name-mapping conventions for class names")))
      (defsection "Description"
	"Used to refer to a known ObjC class by name. (Via the use
	      LOAD-TIME-VALUE, the results of a class-name -> class lookup
	      are cached.)

	      {function objc:@class} is obsolete as of late 2004, because
	      find-class now works on ObjC classes.  It is described here
	      only because some old code still uses it.
	    "))

    (definition (:macro objc:@selector) "@selector string" nil
      (defsection "Arguments and Values"
	(listing :definition
	  (item "{param string}" ccldoc::=> "a string constant, used to canonically refer to an
		        ObjC method selector")))
      (defsection "Description"
	(para "Used to refer to an ObjC method selector (method name). Uses
	      LOAD-TIME-VALUE to cache the result of a string -> selector
	      lookup.")))

    (definition (:macro objc:defmethod)
      "objc:defmethod name-and-result-type ((receiver-arg-and-class) {code &rest} other-args) {code &body} body" nil
      (defsection "Arguments and Values"
	(listing :definition
	  (item "{param name-and-result-type}" ccldoc::=> "either an Objective-C message name, for methods
                that return a value of type {code :ID}, or
                a list containing an Objective-C message name and a
                foreign type specifier for methods with a different
                foreign result type.")
	  (item "{param receiver-arg-and-class}" ccldoc::=> #:|a two-element list whose first element is a
                variable name and whose second element is the Lisp
                name of an Objective-C class or metaclass.  The
                receiver variable name can be any bindable lisp
                variable name, but {code SELF} might be a
                reasonable choice.  The receiver variable is declared
                to be "unsettable"; i.e., it is an error to try to
                change the value of the receiver in the body of the
                method definition.|)
	  (item "{param other-args}" ccldoc::=> "either variable names (denoting parameters of
            type {code :ID}) or 2-element lists whose
            first element is a variable name and whose second element
            is a foreign type specifier.")))
      (defsection "Description"
	(para "Defines an Objective-C-callable method which implements
	        the specified message selector for instances of the existing
	        named Objective-C class.")
	(para "For a detailed description of the features and
          restrictions of the {code OBJC:DEFMETHOD} macro,
          see the
          section "
	  (ref (section "Using objc:defmethod") "Using {code objc:defmethod}") ".")))

    (definition (:macro objc:define-objc-method) "define-objc-method (selector class-name) {code &body} body" nil
      (defsection "Arguments and Values"
	(listing :definition
	  (item "{param selector}" ccldoc::=> "either a string which represents the name of the
		          selector or a list which describes the method's return
		          type, selector components, and argument types (see below.)
		          If the first form is used, then the first form in the body
		          must be a list which describes the selector's argument
		          types and return value type, as per DEFCALLBACK.")
	  (item "{param class-name}" ccldoc::=> #:|either a string which names an existing ObjC class
		          name or a list symbol which can map to such a string via the
		          standard name-mapping conventions for class names. (Note
		          that the "canonical" lisp class name is such a
		          symbol)|)))
      (defsection "Description"
	(para "Defines an ObjC-callable method which implements the
	        specified message selector for instances of the existing ObjC
	        class class-name.")))

    (definition (:macro objc:define-objc-class-method)
      "define-objc-class-method (selector class-name) {code &body} body" nil
      (defsection "Arguments and Values" (para "As per DEFINE-OBJC-METHOD"))
      (defsection "Description"
	#:|Like DEFINE-OBJC-METHOD, only used to define methods on the
	        {emphasis class} named by class-name and on its
	        subclasses.

	        For both DEFINE-OBJC-METHOD and DEFINE-OBJC-CLASS-METHOD, the
	        "selector" argument can be a list whose first element is a
	        foreign type specifier for the method's return value type and whose
	        subsequent elements are either:|
	(listing :bullet
	  (item "a non-keyword symbol, which can be mapped to a selector string
		        for a parameterless method according to the standard name-mapping
		        conventions for method selectors.")
	  (item "a list of alternating keywords and variable/type specifiers,
		        where the set of keywords can be mapped to a selector string for a
		        parameterized method according to the standard name-mapping
		        conventions for method selectors and each variable/type-specifier is
		        either a variable name (denoting a value of type :ID) or a list whose
		        CAR is a variable name and whose CADR is the corresponding
		        argument's foreign type specifier."))))

    (definition (:variable *alternate-line-terminator*) "CCL:*ALTERNATE-LINE-TERMINATOR*" nil
      (defsection "Description"
	#:|This variable is currently only used by the standard reader macro
	        function for #\\; (single-line comments); that function reads successive
	        characters until EOF, a #\\NewLine is read, or a character EQL to the
	        value of *alternate-line-terminator* is read. In {CCL} for Darwin, the
	        value of this variable is initially #\\Return ; in {CCL} for other OSes,
	        it's initially NIL.

	        Their default treatment by the #\\; reader macro is the primary way
	        in which #\\Return and #\\Linefeed differ syntactically; by extending the
	        #\\; reader macro to (conditionally) treat #\\Return as a
	        comment-terminator, that distinction is eliminated. This seems to make
	        LOAD and COMPILE-FILE insensitive to line-termination issues in many
	        cases. It could fail in the (hopefully rare) case where a LF-terminated
	        (Unix) text file contains embedded #\\Return characters, and this
	        mechanism isn't adequate to handle cases where newlines are embedded
	        in string constants or other tokens (and presumably should be translated
	        from an external convention to the external one) : it doesn't change
	        what READ-CHAR or READ-LINE "see", and that may be necessary to
	        handle some more complicated cases.|))

    (definition (:class ccl::ns-lisp-string) "CCL::NS-LISP-STRING" nil
      (defsection "Superclasses" (para "NS:NS-STRING"))
      (defsection "Initargs"
	(listing :definition
	  (item "{param :string}" ccldoc::=> "
		          a Lisp string which is to be the content of
		          the newly-created ns-lisp-string.
		        ")))
      (defsection "Description"
	"
	        This class
	        implements the interface of an NSString, which means that it can
	        be passed to any Cocoa or Core Foundation function which expects
	        one.
	      


	        The string itself is stored on the Lisp heap, which
	        means that its memory management is automatic.  However, the
	        ns-lisp-string object itself is a foreign
	        object (that is, it has an objc metaclass), and resides on the
	        foreign heap.  Therefore, it is necessary to explicitly free
	        it, by sending a dealloc message.
	      ")
      (defsection "Examples"
	(para "
	        You can create an ns-lisp-string with
	        {function make-instance}, just like
	        any normal Lisp class:
	      ")
	(code-block #:|? (defvar *the-string*
     (make-instance 'ccl::ns-lisp-string
                    :string "Hello, Cocoa."))
|)
	(para "
	        When you are done with the string, you must explicitly
	        deallocate it:
	      ")
	(code-block "? (ccl::send *the-string* 'dealloc)")
	(para "
	        You may wish to use an {function unwind-protect}
	        form to ensure that this happens:
	      ")
	(code-block #:|(let (*the-string*)
  (unwind-protect (progn (setq *the-string*
                               (make-instance 'ccl::ns-lisp-string
                                              :string "Hello, Cocoa."))
                         (format t "~&The string is ~D characters long.~%"
                                 (ccl::send *the-string* 'length)))
    (when *the-string*
      (ccl::send *the-string* 'dealloc))))
|))
      (defsection "Notes"
	(para "
	        Currently, ns-lisp-string is defined in
	        the file ccl/examples/cocoa-backtrace.lisp, which is a
	        rather awkward place.  It was probably not originally meant
	        as a public utility at all.  It would be good if it were
	        moved someplace else.  Use at your own risk.
	      ")))))
